package com.congee02.springframework.beans.factory.xml;

import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import com.congee02.springframework.beans.BeansException;
import com.congee02.springframework.beans.PropertyValue;
import com.congee02.springframework.beans.factory.config.BeanDefinition;
import com.congee02.springframework.beans.factory.config.BeanReference;
import com.congee02.springframework.beans.factory.support.AbstractBeanDefinitionReader;
import com.congee02.springframework.beans.factory.support.BeanDefinitionRegistry;
import com.congee02.springframework.core.io.Resource;
import com.congee02.springframework.core.io.ResourceLoader;
import lombok.extern.slf4j.Slf4j;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.io.IOException;
import java.io.InputStream;

@Slf4j
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
        super(registry);
    }

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {
        super(registry, resourceLoader);
    }

    @Override
    public void loadBeanDefinitions(Resource resource) throws BeansException {
        loadBeanDefinitions(new Resource[]{resource});
    }

    @Override
    public void loadBeanDefinitions(Resource... resources) throws BeansException {
        Resource lastReadResource = null;
        try {
            for (Resource resource : resources) {
                log.debug("尝试读取 resource: {}", resource);
                try (InputStream inputStream = resource.getInputStream()) {
                    lastReadResource = resource;
                    doLoadBeanDefinitions(inputStream);
                }
            }
        } catch (ClassNotFoundException | IOException e) {
            throw new BeansException("Error parsing xml bean definition file from " + lastReadResource, e);
        }
    }

    @Override
    public void loadBeanDefinitions(String location) throws BeansException {
        ResourceLoader resourceLoader = getResourceLoader();
        Resource resource = resourceLoader.getResource(location);
        log.debug("尝试读取 resource: {}", resource);
        loadBeanDefinitions(resource);
    }

    @Override
    public void loadBeanDefinitions(String... locations) throws BeansException {
        for (String location : locations) {
            loadBeanDefinitions(location);
        }
    }

    protected void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException {
        Document doc = XmlUtil.readXML(inputStream);
        Element root = doc.getDocumentElement();
        NodeList childNodes = root.getChildNodes();

        for (int i = 0 ; i < childNodes.getLength() ; i ++ ) {
            Node currentChildNode = childNodes.item(i);
            if (! (currentChildNode instanceof Element bean) || ! ("bean".equals(currentChildNode.getNodeName()))) {
                continue;
            }

            String id = bean.getAttribute("id");
            String name = bean.getAttribute("name");
            String clazzName = bean.getAttribute("class");
            Class<?> clazz = Class.forName(clazzName);
            String beanName = StrUtil.isNotEmpty(id) ? id : name;
            if (StrUtil.isEmpty(beanName)) {
                beanName = StrUtil.lowerFirst(clazz.getSimpleName());
            }
            if (getRegistry().containsBeanDefinition(beanName)) {
                throw new BeansException("Duplicate beanName[" + beanName + "] is not allowed");
            }

            log.debug("解析 XML 中的 bean id: {}, name: {}, clazz: {}", id, name, clazz);

            BeanDefinition beanDefinition = new BeanDefinition(clazz);
            NodeList beanChildNodes = bean.getChildNodes();
            for (int j = 0; j < beanChildNodes.getLength() ; j ++ ) {
                Node currentBeanChildNode = beanChildNodes.item(j);
                if (! (currentBeanChildNode instanceof Element property) || ! ("property".equals(currentBeanChildNode.getNodeName()))) {
                    continue;
                }

                String attrName = property.getAttribute("name");
                String attrValue = property.getAttribute("value");
                String attrRef = property.getAttribute("ref");

                Object value = StrUtil.isNotEmpty(attrRef) ? new BeanReference(attrRef) : attrValue;
                PropertyValue propertyValue = new PropertyValue(attrName, value);
                beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
            }

            beanDefinition.setInitMethodName(bean.getAttribute("init-method"));
            beanDefinition.setDestroyMethodName(bean.getAttribute("destroy-method"));

            String scope = bean.getAttribute("scope");
            if (StrUtil.isNotEmpty(scope)) {
                beanDefinition.setScope(scope);
            }

            log.debug("解析为 BeanDefinition: {}", beanDefinition);

            getRegistry().registerBeanDefinition(beanName, beanDefinition);

        }

    }

}
